package ltd.dujiabao.inventoryservice.service.impl;

import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import lombok.extern.slf4j.Slf4j;
import ltd.dujiabao.inventoryservice.dao.InventoryFreezeRepository;
import ltd.dujiabao.inventoryservice.dao.InventoryRepository;
import ltd.dujiabao.inventoryservice.domain.entity.InventoryFreeze;
import ltd.dujiabao.inventoryservice.service.InventoryTccService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * @author Bowie Du
 * @date 2025/7/22 20:09
 */
@Slf4j
@Service
public class InventoryTccServiceImpl implements InventoryTccService {
    @Autowired
    private InventoryRepository inventoryRepository;

    @Autowired
    private InventoryFreezeRepository inventoryFreezeRepository;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deductStock(@BusinessActionContextParameter(paramName = "productId") Long productId,
                            @BusinessActionContextParameter(paramName = "quantity") Integer quantity) {
        String xid = RootContext.getXID();
        // 插入冻结记录。xid有唯一键，可以保证幂等性，避免业务悬挂（在try之前已经cancel了）
        InventoryFreeze inventoryFreeze = new InventoryFreeze()
                .setXid(xid)
                .setStatus(InventoryFreeze.Status.TRY)
                .setProductId(productId)
                .setFreezyStock(quantity)
                .setCreatedAt(LocalDateTime.now())
                .setUpdatedAt(LocalDateTime.now());
        inventoryFreezeRepository.save(inventoryFreeze);

        // 扣除库存
        boolean isSuccess = inventoryRepository.deductStock(productId, quantity) > 0;
        if (!isSuccess) {
            throw new RuntimeException("fail to deduct stock.");
        }
        log.info("success try xid {}", xid);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean commit(BusinessActionContext context) {
        String xid = context.getXid();
        boolean isSuccess = inventoryFreezeRepository.updateStatusByXidAndStatus(
                xid, InventoryFreeze.Status.TRY, InventoryFreeze.Status.CONFIRM) > 0;
        log.info("commit xid {} result {}", xid, isSuccess);
        return isSuccess;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean rollback(BusinessActionContext context) {
        String xid = context.getXid();
        Long productId = context.getActionContext("productId", Long.class);
        Integer quantity = context.getActionContext("quantity", Integer.class);
        // 避免出现重复回滚、空回滚的情况，只在预期状态下才进行状态更新及回滚
        boolean isSuccess = inventoryFreezeRepository.updateStatusByXidAndStatus(
                xid, InventoryFreeze.Status.TRY, InventoryFreeze.Status.CANCEL) > 0;
        log.info("rollback xid {} update status result {}", xid, isSuccess);
        // 回滚
        if (isSuccess) {
            boolean restoreResult = inventoryRepository.restoreStock(productId, quantity) > 0;
            log.info("restore stock result {}", restoreResult);
            return true;
        }

        // 若更新失败，则需确认是否没有记录，对于没有记录的情况，需要插入一条冻结记录，以避免空悬挂的情况
        boolean freezyExists = inventoryFreezeRepository.existsByXid(xid);
        if (!freezyExists) {
            InventoryFreeze inventoryFreeze = new InventoryFreeze()
                    .setXid(xid)
                    .setStatus(InventoryFreeze.Status.CANCEL)
                    .setProductId(productId)
                    .setFreezyStock(quantity)
                    .setCreatedAt(LocalDateTime.now())
                    .setUpdatedAt(LocalDateTime.now());
            inventoryFreezeRepository.save(inventoryFreeze);
            log.info("add freezy log");
        }
        return true;
    }
}
